var crypto  = require('crypto');
var http    = require('http');
global.clone = require('clone');

global.INT8_MAX = Number(0x7F);
global.UINT8_MAX = Number(0xFF);
global.INT16_MAX = Number(0x7FFF);
global.UINT16_MAX = Number(0xFFFF);
global.INT32_MAX = Number(0x7FFFFFFF);
global.UINT32_MAX = Number(0xFFFFFFFF);

global.LOG = function(content) {
    console.log((new Date()).format('[yyyy-MM-dd hh:mm:ss] '), content);
};

global.INFO = function(content) {
    console.error((new Date()).format('[yyyy-MM-dd hh:mm:ss] INFO '), content);
};

global.ERROR = function(content) {
    console.error((new Date()).format('[yyyy-MM-dd hh:mm:ss] ERROR '), content);
};


Array.prototype.sum = function() {
    var total = 0;
    for(var i=0; i<this.length; i++) {
        if( !isNaN(this[i]) ) {
            total += +this[i];
        }
    }

    return total;
}

Array.prototype.remove = function(item) {
    var index = this.indexOf(item);

    if( index >= 0){
        this.splice(index, 1);
    }
}

Array.prototype.split = function(partLength) {
    if (partLength <= 1) {
        return this;
    }

    var arrs = [];
    var length = this.length;
    for (var i = 0; i < length; i+= partLength) {
        arrs.push(this.slice(i, i + partLength));
    }
    return arrs;
}


String.prototype.startWith = function(prefix) {
    if( !prefix || !this.length || this[0] != prefix[0] ) return false;
    return (this.substr(0, prefix.length) == prefix);
}

String.prototype.endWith = function(suffix) {
    if( !suffix || !this.length || suffix.length > this.length) return false;
    return (this.substr(this.length - suffix.length) == suffix);
}

String.prototype.format = function() {
    var args = arguments;
    return this.replace(/\{(\d+)\}/g, function(m,i){
        return args[i];
    });
}

String.prototype.capWord = function() {
    return this.substr(0,1).toUpperCase() + this.substr(1);
}


Date.prototype.format = function(fmt)   {
    var o = {   
        'M+' : this.getMonth()+1,                 //月份   
        'd+' : this.getDate(),                    //日   
        'h+' : this.getHours(),                   //小时   
        'm+' : this.getMinutes(),                 //分   
        's+' : this.getSeconds(),                 //秒   
        'q+' : Math.floor((this.getMonth()+3)/3), //季度   
        'S'  : this.getMilliseconds()             //毫秒   
    };   
    if(/(y+)/.test(fmt)) {
        fmt=fmt.replace(RegExp.$1, (this.getFullYear()+'').substr(4 - RegExp.$1.length));   
    }
    for(var k in o) {
        if(new RegExp('('+ k +')').test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? 
                    (o[k]) : (('00'+ o[k]).substr((''+ o[k]).length)));   
        }
    }
    return fmt;   
} 

Date.prototype.getUTCFullMilliseconds = function() {
    return this.getTime() - this.getTimezoneOffset()*60000;
}

Date.prototype.getDays = function() {
    return Math.floor(this.getUTCFullMilliseconds()/86400000);
}

Date.prototype.getFullMonth = function() {
    return this.getFullYear()*12 + this.getMonth();
}

Date.prototype.getDayPassed = function(refTime) {
    // 获取距离refTime经过的天数, refTime(秒|2013-01-01|2013-01-01 00:00:00)
    if( isNaN(refTime) ) {
        if( refTime.length > 10 ) {
            refTime = refTime.substr(0, 10);
        }
        refTime = new Date(refTime + ' 00:00:00');
    }else{
        refTime = new Date(refTime * 1000);
    }

    refTime.setHours(0);
    refTime.setMinutes(0);
    refTime.setSeconds(0);

    return Math.ceil((this - refTime)/1000/86400);
}

Date.prototype.getUTCStamp = function() {
    return Math.floor(this.getUTCFullMilliseconds()/1000);
}

Date.prototype.getStamp = function() {
    return Math.floor(this.getTime()/1000);
}

Date.prototype.setFloatTime = function(hour, minute, second) {
    var totalMinutes = Math.ceil(hour*3600 + minute*60 + second);
    hour = Math.floor(totalMinutes/3600);
    totalMinutes = totalMinutes%3600;
    minute = Math.floor(totalMinutes/60);
    second = totalMinutes%60;
    
    this.setHours(hour);
    this.setMinutes(minute);
    this.setSeconds(second);
    return this;
}

Date.prototype.zeroTime = function() {
    return new Date(this.getFullYear(), this.getMonth(), this.getDate());
}

Date.prototype.zeroMonth = function() {
    return new Date(this.getFullYear(), this.getMonth(), 1);
}

Date.prototype.relativeTime = function(seconds) {
    return new Date(this.getTime() + seconds*1000); 
}

Date.prototype.stdFormatedString = function() {
    return this.format('yyyy-MM-dd hh:mm:ss');
}

Date.prototype.getDateNumber = function() {
    return this.getFullYear()*10000 + (this.getMonth()+1)*100 + this.getDate();
}

Date.nextMonth = function(stamp){
    var _d = Date.createFromStamp(stamp).zeroMonth();
    _d.setMonth(_d.getMonth() + 1);
    return _d.getStamp();
};

Date.prevMonth = function(stamp){
    var _d = Date.createFromStamp(stamp).zeroMonth();
    _d.setMonth(_d.getMonth() - 1);
    return _d.getStamp();
};

Date.getDayPassed = function(refTime) {
    return (new Date()).getDayPassed(refTime);
}

Date.getStamp = function() {
    return (new Date()).getStamp();
}

Date.createFromStamp = function(stamp) {
    return new Date(stamp*1000);
}

Date.stdFormatedString = function() {
    return (new Date()).stdFormatedString();
}


global.addAttrValue = function(obj, attr, value) {
    var newValue = (obj[attr] || 0) + value;
    obj[attr] = newValue;
    return newValue;
}

global.getValueWithDefault = function(value, defaultValue) {
    return (null != value) ? value : defaultValue;
}

global.getAttrValue = function(obj, attr, defaultValue) {
    var value = obj[attr];
    return (null != value) ? value : defaultValue;
}

global.exCloneObj = function(obj) {
    var o;

    if (null == obj || typeof(obj) != 'object') {
        o = obj;
    } else {
        if (obj instanceof Array) {
            o = [];
            for (var i = 0, len = obj.length; i < len; ++i) {
                o.push(exCloneObj(obj[i]));
            }
        } else {
            o = {};
            for (var k in obj) {
                o[k] = exCloneObj(obj[k]);
            }
        }
    }

    return o;
}

global.objectForEach = function(obj, callback) {
	var keys = Object.keys(obj);
	for (var i = 0, len = keys.length; i < len; ++i) {
		var k = keys[i];
		callback(k, obj[k]);
	}
}

global.objectForEachKey = function(obj, callback) {
	var keys = Object.keys(obj);
	for (var i = 0, len = keys.length; i < len; ++i) {
		callback(keys[i]);
	}
}

global.List = function(obj) {
    this.obj = obj;
    this.next = null;
}

global.gGetSidFromUid = function(uid) {
    var sid = Math.floor(uid/1000000);
    if (uid > 100000000) {
        sid = Math.floor((uid-100000000)/100000);
    }
    return sid - 1;
};


// 上个月相同的时间
function getLastMonthSameDate(refDate) {
    var lastDate = new Date(refDate);
    if (refDate.getMonth() > 0) {
        lastDate.setMonth(refDate.getMonth() - 1);
    } else {
        lastDate.setFullYear(refDate.getFullYear() - 1);
        lastDate.setMonth(11);    
    }
    
    return lastDate;
}

function getTime() {
    return Math.round(+(new Date()) / 1000);
}

function getDate() {
    var now = new Date();
    return now.getFullYear() * 10000 + (now.getMonth()+1) * 100 + now.getDate();
}

function getDateString() {
    // 返回当前日期的字符串,eg:"2013-01-01"
    return (new Date).format('yyyy-MM-dd');
}

function getDayPassed(date1) {
    // 获取当前时间距离date1经过的天数[date1, now], date1(秒|2013-01-01|2013-01-01 00:00:00)
    // now >= date1
    if( isNaN(date1) ) {
        if( date1.length > 10 ) {
            date1 = date1.substr(0, 10);
        }
        date1 = new Date(date1 + ' 00:00:00');
    }else{
        date1 = new Date(date1 * 1000);
    }

    date1.setHours(0);
    date1.setMinutes(0);
    date1.setSeconds(0);

    return Math.ceil((new Date() - date1)/1000/86400);
}

function getDayDiff(date1,date2) {
    // 返回两个日期"2013-01-01"00时00秒之间的天数
    var dateTime1 = Math.floor(Date.parse(date1));
    var dateTime2 = Math.floor(Date.parse(date2));
    return Math.floor(Math.abs((dateTime2 -dateTime1)/1000/60/60/24));
}

function isDayDiffContinue(day1,day2) {
    // 判断day1 和 day2 是否连续 day1 eg:20130101整数
    var date1 = day1.toString();   
    var date2 = day2.toString();
    date1 = date1.slice(0, 4) + '-' + date1.slice(4, 6) + '-' + date1.slice(6);
    date2 = date2.slice(0, 4) + '-' + date2.slice(4, 6) + '-' + date2.slice(6);

    var dateTime1 = Math.floor(Date.parse(date1));
    var dateTime2 = Math.floor(Date.parse(date2));
    
    return ( Math.floor(Math.abs((dateTime2 -dateTime1)/1000/60/60/24)) <= 1 ) ? 1 : 0;
}

function getDayDiffDays(day1, day2) {
    var date1 = day1.toString();   
    var date2 = day2.toString();
    date1 = date1.slice(0, 4) + '-' + date1.slice(4, 6) + '-' + date1.slice(6);
    date2 = date2.slice(0, 4) + '-' + date2.slice(4, 6) + '-' + date2.slice(6);

    var dateTime1 = Math.floor(Date.parse(date1));
    var dateTime2 = Math.floor(Date.parse(date2));
    
    return Math.floor(Math.abs((dateTime2 -dateTime1)/1000/86400));
}


function wRand(weights) {
    var total = 0;
    for( var key in weights ) {
        total += weights[key];
    }

    var rand = Math.random() * total;
    var offset = 0;
    for( var key in weights ) {
        offset += weights[key];
        if( rand <= offset ) {
            return key;
        }
    }

    return null;
}

// [from, to]
function randRange(from, to) {
    return from + Math.floor(Math.random() * (to - from + 1));  
}

function randArray(arr) {
    return arr[randRange(0, arr.length-1)];
}

function randValue(max) {
    return Math.floor(Math.random() * max);
}


var authKey = 'Keep it simple stupid';
function genAuth(uid){
    var now = common.getTime();
    var md5 = crypto.createHmac('sha1', authKey);
    var rawKey = uid + '-' + now;

    var key = md5.update(rawKey).digest('base64').substring(0, 10);
    return {'key':key, 'time':now};
}

function verifyAuth(uid, key, time) {
    var now = common.getTime();
    var md5 = crypto.createHmac('sha1', authKey);
    var rawKey = uid + '-' + time;

    if( key != md5.update(rawKey).digest('base64').substring(0, 10) ) {
        return false;
    }

    if( now < time || (now - time) >= 7202 ) {
        return false;
    }

    return true;
}

function verifyGatewayAuth(openid, key) {
    var md5 = crypto.createHmac('sha1', authKey);
    return key == md5.update(openid, 'utf8').digest('base64').substring(0, 10);
}

function verifyWordFilter(content, key) {
    var md5 = crypto.createHmac('sha1', authKey);
    return key == md5.update(content, 'utf8').digest('base64').substring(0, 10);
}

function verifyGameHack(uid, uKey, seq, mod, act, key) {
    //var source = util.format('%d-%d-%s-%s-%s', uid, seq, mod, act, "!*%#@^&xyz");
    var source = util.format('%d-%d-%s-%s-%s', uid, seq, mod, act, uKey);
    var md5 = crypto.createHash('md5');
    return key == md5.update(source).digest('hex').substring(0, 10);
}

function getGameHackKey(uid) {
    var today = new Date((common.getTime() - 3600*(uid%24))*1000).format('yyyy-MM-dd');
    var md5 = crypto.createHmac('sha1', authKey);
    return md5.update(uid + today, 'utf8').digest('base64').substring(0, 10)
}


function httpGet(url, callback, json){
    http.get(url, function(res){
        var chunks = [];
        res.on('data', function(chunk){
            chunks.push(chunk);
        });

        res.on('end', function(){
            var data = Buffer.concat(chunks).toString();
            if( json ) {
                try{
                    data = JSON.parse(data);
                }catch(e){
                    data = null;
                }
            }
            callback && callback(data);
        });
    }).on('error', function(e){
        if( json ) {
            callback && callback(null);
        }else{
            callback && callback(e);
        }
    });
}

function Loader(onAllLoad){
    this.tasks = [];
    this.onAllLoad = onAllLoad;
}

Loader.prototype = {
    addLoad : function(task){
        this.tasks.push(task);
    },

    onLoad : function(task){
        var index = this.tasks.indexOf(task);
        if( index >= 0 ) {
            this.tasks.splice(index, 1);
            if( this.tasks.length == 0 ) {
                this.onAllLoad && this.onAllLoad();
            }
        }
    },

    done : function() {
        if (this.tasks.length == 0) {
            this.onAllLoad && this.onAllLoad();
        }
    }
};

function handleHeadpicParseException(headpic) {
    return (typeof(headpic) == 'string' ? headpic.slice(0, headpic.indexOf('&')>=0 ? headpic.indexOf('&') : headpic.length) : headpic);
}

exports.getLastMonthSameDate = getLastMonthSameDate;
exports.getTime = getTime;
exports.getDate = getDate;
exports.wRand = wRand;
exports.randRange = randRange;
exports.randArray = randArray;
exports.randValue = randValue;

exports.defaultHeaders = {
    'Content-Type': 'text/plain;charset=utf-8', 
//    'Connection': 'close'
};
exports.gzipHeaders = {
    'Content-Type': 'text/plain;charset=utf-8', 
//    'Connection': 'close',
    'Content-Encoding': 'gzip'
};
exports.htmlHeaders = {
    'Content-Type': 'text/html;charset=utf-8',
    'Connection': 'close'
};
exports.defaultCrossDomain = '<cross-domain-policy><allow-access-from domain="*" secure="true" /></cross-domain-policy>';

exports.authKey = authKey;
exports.genAuth = genAuth;
exports.verifyAuth = verifyAuth;
exports.verifyGatewayAuth = verifyGatewayAuth;
exports.verifyWordFilter = verifyWordFilter;
exports.httpGet = httpGet;
exports.getDateString = getDateString;
exports.getDayDiff = getDayDiff;
exports.Loader = Loader;
exports.isDayDiffContinue = isDayDiffContinue;
exports.getDayDiffDays = getDayDiffDays;
exports.getDayPassed = getDayPassed;
exports.verifyGameHack = verifyGameHack;
exports.getGameHackKey = getGameHackKey;
exports.handleHeadpicParseException = handleHeadpicParseException;

